#pragma once
#ifndef __HWINEXCEPTION_H__
#define __HWINEXCEPTION_H__

#include "hwindef.h"


namespace harlinn
{
    namespace windows
    {
        class ExceptionData
        {
            std::string message;
            std::string helpLink;
            std::string source;
            HRESULT hResult;
            long long code;
            ExceptionData* innerException;
            
        public:
            HWIN_EXPORT explicit ExceptionData();
            HWIN_EXPORT explicit ExceptionData(const ExceptionData& theInnerException);
            HWIN_EXPORT explicit ExceptionData(const char* theMessage);
            HWIN_EXPORT explicit ExceptionData(const std::string& theMessage);
            HWIN_EXPORT ExceptionData(const char* theMessage, const ExceptionData* theInnerException);
            HWIN_EXPORT ~ExceptionData( );

            HWIN_EXPORT ExceptionData* Clone() const;

            HWIN_EXPORT const std::string& GetMessage() const;
            HWIN_EXPORT std::string GetMessage();
            HWIN_EXPORT ExceptionData& SetMessage(const char* theValue);
            HWIN_EXPORT ExceptionData& SetMessage(const std::string& theValue);

            HWIN_EXPORT const std::string& GetHelpLink() const;
            HWIN_EXPORT std::string GetHelpLink();
            HWIN_EXPORT ExceptionData& SetHelpLink(const char* theValue);
            HWIN_EXPORT ExceptionData& SetHelpLink(const std::string& theValue);

            HWIN_EXPORT HRESULT GetHRESULT() const;
            HWIN_EXPORT ExceptionData& SetHRESULT(HRESULT theValue);

            HWIN_EXPORT long long GetCode() const;
            HWIN_EXPORT ExceptionData& SetCode(long long theValue);


            HWIN_EXPORT const ExceptionData* GetInnerException() const;
            HWIN_EXPORT ExceptionData& SetInnerException(const ExceptionData* theValue);

            HWIN_EXPORT const std::string& GetSource() const;
            HWIN_EXPORT std::string GetSource();
            HWIN_EXPORT ExceptionData& SetSource(const char* theValue);
            HWIN_EXPORT ExceptionData& SetSource(const std::string& theValue);
        };

        class Exception : std::exception
        {
            ExceptionData* data;
            HWIN_EXPORT static std::string empty;
            HWIN_EXPORT const ExceptionData* GetExceptionData() const;
            HWIN_EXPORT ExceptionData* GetExceptionData();
        public:
            HWIN_EXPORT static std::string Format(const char*fmt, ... );
        protected:
            HWIN_EXPORT static std::string FormatList(const char*fmt, va_list args );
        public:
            HWIN_EXPORT explicit Exception();
            HWIN_EXPORT Exception(const Exception& theException);
            HWIN_EXPORT Exception(Exception&& theException);
            HWIN_EXPORT explicit Exception(const char* theMessage);
            HWIN_EXPORT explicit Exception(const std::string& theMessage);
            HWIN_EXPORT Exception(const char* theMessage, const Exception& theInnerException);
            HWIN_EXPORT virtual ~Exception();

            HWIN_EXPORT Exception& operator = (const Exception& theException);
            HWIN_EXPORT Exception& operator = (Exception&& theException);

            HWIN_EXPORT virtual const char* what() const;


            HWIN_EXPORT const std::string& GetMessage() const;
            HWIN_EXPORT std::string GetMessage();
            HWIN_EXPORT Exception& SetMessage(const char* theValue);
            HWIN_EXPORT Exception& SetMessage(const std::string& theValue);

            HWIN_EXPORT const std::string& GetHelpLink() const;
            HWIN_EXPORT std::string GetHelpLink();
            HWIN_EXPORT Exception& SetHelpLink(const char* theValue);
            HWIN_EXPORT Exception& SetHelpLink(const std::string& theValue);

            HWIN_EXPORT HRESULT GetHRESULT() const;
            HWIN_EXPORT Exception& SetHRESULT(HRESULT theValue);

            HWIN_EXPORT long long GetCode() const;
            HWIN_EXPORT Exception& SetCode(long long theValue);

            HWIN_EXPORT const ExceptionData* GetInnerException() const;
            HWIN_EXPORT Exception& SetInnerException(const Exception& theValue);

            HWIN_EXPORT const std::string& GetSource() const;
            HWIN_EXPORT std::string GetSource();
            HWIN_EXPORT Exception& SetSource(const char* theValue);
            HWIN_EXPORT Exception& SetSource(const std::string& theValue);

        };

#define HARLINN_WINDOWS_DECLARE_STANDARD_EXCEPTION_MEMBERS( className ) \
HWIN_EXPORT explicit className (); \
HWIN_EXPORT className (const className & theException);  \
HWIN_EXPORT className ( className && theException);  \
HWIN_EXPORT explicit className (const char* theMessage);  \
HWIN_EXPORT explicit className (const std::string& theMessage);  \
HWIN_EXPORT className (const char* theMessage, const Exception& theInnerException);  \
HWIN_EXPORT className & operator = (const className & theException); \
HWIN_EXPORT className & operator = ( className && theException)

#define HARLINN_WINDOWS_IMPLEMENT_STANDARD_EXCEPTION_MEMBERS( className ) \
HWIN_EXPORT className :: className () : Base() { } \
HWIN_EXPORT className :: className (const className & theException) : Base(theException) { }  \
HWIN_EXPORT className :: className ( className && theException) : Base(theException) { }  \
HWIN_EXPORT className :: className (const char* theMessage) : Base(theMessage) { }  \
HWIN_EXPORT className :: className (const std::string& theMessage) : Base(theMessage) { }  \
HWIN_EXPORT className :: className (const char* theMessage, const Exception& theInnerException) : Base(theMessage,theInnerException) { }  \
HWIN_EXPORT className & className :: operator = (const className & theException ) { Base :: operator = ( reinterpret_cast< const Base & > (theException) ); return *this; }  \
HWIN_EXPORT className & className :: operator = ( className && theException) { Base :: operator = ( reinterpret_cast< Base && > (theException) ); return *this; }  

        class SystemException : public Exception
        {
        public:
            typedef Exception Base;
            HARLINN_WINDOWS_DECLARE_STANDARD_EXCEPTION_MEMBERS( SystemException );
        };

        class AccessViolationException : public SystemException
        {
        public:
            typedef SystemException Base;
            HARLINN_WINDOWS_DECLARE_STANDARD_EXCEPTION_MEMBERS( AccessViolationException );
        };

        class AggregateException : public Exception
        {
        public:
            typedef Exception Base;
            HARLINN_WINDOWS_DECLARE_STANDARD_EXCEPTION_MEMBERS( AggregateException );
        };

        class ApplicationException  : public Exception
        {
        public:
            typedef Exception Base;
            HARLINN_WINDOWS_DECLARE_STANDARD_EXCEPTION_MEMBERS( ApplicationException );
        };


        class ArgumentException : public SystemException
        {
        public:
            typedef SystemException Base;

            HARLINN_WINDOWS_DECLARE_STANDARD_EXCEPTION_MEMBERS( ArgumentException );

            HWIN_EXPORT ArgumentException(const char* message, const char* argument);
            HWIN_EXPORT ArgumentException(const std::string& message, const char* argument );
            HWIN_EXPORT ArgumentException(const std::string& message, const std::string& argument);
        };

        class ArgumentNullException : public ArgumentException
        {
        public:
            typedef ArgumentException Base;
            HARLINN_WINDOWS_DECLARE_STANDARD_EXCEPTION_MEMBERS( ArgumentNullException );

            

        };

        class ArgumentOutOfRangeException  : public ArgumentException
        {
        public:
            typedef ArgumentException Base;
            HARLINN_WINDOWS_DECLARE_STANDARD_EXCEPTION_MEMBERS( ArgumentOutOfRangeException );

            HWIN_EXPORT ArgumentOutOfRangeException(const char* argument, const char* message);
            HWIN_EXPORT ArgumentOutOfRangeException(const char* argument, const std::string& message);
            HWIN_EXPORT ArgumentOutOfRangeException(const std::string& argument, const std::string& message);

        };


        class ArithmeticException : public SystemException
        {
        public:
            typedef SystemException Base;
            HARLINN_WINDOWS_DECLARE_STANDARD_EXCEPTION_MEMBERS( ArithmeticException );

        };

        class BadImageFormatException : public SystemException
        {
        public:
            typedef SystemException Base;

            HARLINN_WINDOWS_DECLARE_STANDARD_EXCEPTION_MEMBERS( BadImageFormatException );
        };


        class DataMisalignedException : public SystemException
        {
        public:
            typedef SystemException Base;

            HARLINN_WINDOWS_DECLARE_STANDARD_EXCEPTION_MEMBERS( DataMisalignedException );
        };

        class TypeLoadException : public SystemException
        {
        public:
            typedef SystemException Base;

            HARLINN_WINDOWS_DECLARE_STANDARD_EXCEPTION_MEMBERS( TypeLoadException );
        };

        class DivideByZeroException : public ArithmeticException
        {
        public:
            typedef ArithmeticException Base;

            HARLINN_WINDOWS_DECLARE_STANDARD_EXCEPTION_MEMBERS( DivideByZeroException );
        };

        class DllNotFoundException : public TypeLoadException
        {
        public:
            typedef TypeLoadException Base;

            HARLINN_WINDOWS_DECLARE_STANDARD_EXCEPTION_MEMBERS( DllNotFoundException );
        };

        class DuplicateWaitObjectException : public ArgumentException
        {
        public:
            typedef ArgumentException Base;

            HARLINN_WINDOWS_DECLARE_STANDARD_EXCEPTION_MEMBERS( DuplicateWaitObjectException );
        };

        class EntryPointNotFoundException : public TypeLoadException
        {
        public:
            typedef TypeLoadException Base;

            HARLINN_WINDOWS_DECLARE_STANDARD_EXCEPTION_MEMBERS( EntryPointNotFoundException );
        };

        class MemberAccessException : public SystemException
        {
        public:
            typedef SystemException Base;

            HARLINN_WINDOWS_DECLARE_STANDARD_EXCEPTION_MEMBERS( MemberAccessException );
        };

        class FieldAccessException : public MemberAccessException
        {
        public:
            typedef MemberAccessException Base;

            HARLINN_WINDOWS_DECLARE_STANDARD_EXCEPTION_MEMBERS( FieldAccessException );
        };


        class FormatException : public SystemException
        {
        public:
            typedef SystemException Base;

            HARLINN_WINDOWS_DECLARE_STANDARD_EXCEPTION_MEMBERS( FormatException );
        };

        class IndexOutOfRangeException : public SystemException
        {
        public:
            typedef SystemException Base;

            HARLINN_WINDOWS_DECLARE_STANDARD_EXCEPTION_MEMBERS( IndexOutOfRangeException );
        }; 


        class InsufficientExecutionStackException : public SystemException
        {
        public:
            typedef SystemException Base;

            HARLINN_WINDOWS_DECLARE_STANDARD_EXCEPTION_MEMBERS( InsufficientExecutionStackException );
        };

        class OutOfMemoryException : public SystemException
        {
        public:
            typedef SystemException Base;

            HARLINN_WINDOWS_DECLARE_STANDARD_EXCEPTION_MEMBERS( OutOfMemoryException );

        }; 

        class InsufficientMemoryException : public OutOfMemoryException
        {
        public:
            typedef OutOfMemoryException Base;

            HARLINN_WINDOWS_DECLARE_STANDARD_EXCEPTION_MEMBERS( InsufficientMemoryException );
        }; 

        class InvalidCastException : public SystemException
        {
        public:
            typedef SystemException Base;

            HARLINN_WINDOWS_DECLARE_STANDARD_EXCEPTION_MEMBERS( InvalidCastException );
        }; 


        class InvalidOperationException : public SystemException
        {
        public:
            typedef SystemException Base;

            HARLINN_WINDOWS_DECLARE_STANDARD_EXCEPTION_MEMBERS( InvalidOperationException );
        };

        class InvalidProgramException : public SystemException
        {
        public:
            typedef SystemException Base;

            HARLINN_WINDOWS_DECLARE_STANDARD_EXCEPTION_MEMBERS( InvalidProgramException );
        };

        class InvalidTimeZoneException : public Exception
        {
        public:
            typedef Exception Base;

            HARLINN_WINDOWS_DECLARE_STANDARD_EXCEPTION_MEMBERS( InvalidTimeZoneException );
        };

        class MethodAccessException : public MemberAccessException
        {
        public:
            typedef MemberAccessException Base;

            HARLINN_WINDOWS_DECLARE_STANDARD_EXCEPTION_MEMBERS( MethodAccessException );
        };

        class MissingMemberException : public MemberAccessException
        {
        public:
            typedef MemberAccessException Base;

            HARLINN_WINDOWS_DECLARE_STANDARD_EXCEPTION_MEMBERS( MissingMemberException );
        };

        class MissingFieldException : public MissingMemberException
        {
        public:
            typedef MissingMemberException Base;

            HARLINN_WINDOWS_DECLARE_STANDARD_EXCEPTION_MEMBERS( MissingFieldException );
        };


        class MissingMethodException : public MissingMemberException
        {
        public:
            typedef MissingMemberException Base;

            HARLINN_WINDOWS_DECLARE_STANDARD_EXCEPTION_MEMBERS( MissingMethodException );
        };

        class MulticastNotSupportedException : public SystemException
        {
        public:
            typedef SystemException Base;

            HARLINN_WINDOWS_DECLARE_STANDARD_EXCEPTION_MEMBERS( MulticastNotSupportedException );
        };

        class NotFiniteNumberException : public ArithmeticException
        {
        public:
            typedef ArithmeticException Base;

            HARLINN_WINDOWS_DECLARE_STANDARD_EXCEPTION_MEMBERS( NotFiniteNumberException );
        };

        class NotImplementedException : public SystemException
        {
        public:
            typedef SystemException Base;

            HARLINN_WINDOWS_DECLARE_STANDARD_EXCEPTION_MEMBERS( NotImplementedException );
        };

        class NotSupportedException : public SystemException
        {
        public:
            typedef SystemException Base;

            HARLINN_WINDOWS_DECLARE_STANDARD_EXCEPTION_MEMBERS( NotSupportedException );
        };

        class NullReferenceException : public SystemException
        {
        public:
            typedef SystemException Base;

            HARLINN_WINDOWS_DECLARE_STANDARD_EXCEPTION_MEMBERS( NullReferenceException );
        };

        class OperationCanceledException : public SystemException
        {
        public:
            typedef SystemException Base;

            HARLINN_WINDOWS_DECLARE_STANDARD_EXCEPTION_MEMBERS( OperationCanceledException );
        };

        class OverflowException : public ArithmeticException
        {
        public:
            typedef ArithmeticException Base;

            HARLINN_WINDOWS_DECLARE_STANDARD_EXCEPTION_MEMBERS( OverflowException );
        };


        class PlatformNotSupportedException : public NotSupportedException
        {
        public:
            typedef NotSupportedException Base;

            HARLINN_WINDOWS_DECLARE_STANDARD_EXCEPTION_MEMBERS( PlatformNotSupportedException );
        };

        class RankException : public SystemException
        {
        public:
            typedef SystemException Base;

            HARLINN_WINDOWS_DECLARE_STANDARD_EXCEPTION_MEMBERS( RankException );
        };

        class TimeoutException : public SystemException
        {
        public:
            typedef SystemException Base;

            HARLINN_WINDOWS_DECLARE_STANDARD_EXCEPTION_MEMBERS( TimeoutException );
        };


        class TypeInitializationException : public SystemException
        {
        public:
            typedef SystemException Base;

            HARLINN_WINDOWS_DECLARE_STANDARD_EXCEPTION_MEMBERS( TypeInitializationException );
        };

        class TypeUnloadedException : public SystemException
        {
        public:
            typedef SystemException Base;

            HARLINN_WINDOWS_DECLARE_STANDARD_EXCEPTION_MEMBERS( TypeUnloadedException );
        };

        class UnauthorizedAccessException : public SystemException
        {
        public:
            typedef SystemException Base;

            HARLINN_WINDOWS_DECLARE_STANDARD_EXCEPTION_MEMBERS( UnauthorizedAccessException );
        };

        class UriFormatException : public FormatException
        {
        public:
            typedef FormatException Base;

            HARLINN_WINDOWS_DECLARE_STANDARD_EXCEPTION_MEMBERS( UriFormatException );
        };

        class UriTemplateMatchException : public SystemException
        {
        public:
            typedef SystemException Base;

            HARLINN_WINDOWS_DECLARE_STANDARD_EXCEPTION_MEMBERS( UriTemplateMatchException );
        };

        namespace io
        {
            class IOException : public SystemException
            {
            public:
                typedef SystemException Base;

                HARLINN_WINDOWS_DECLARE_STANDARD_EXCEPTION_MEMBERS( IOException );
            };

            class DirectoryNotFoundException : public IOException
            {
            public:
                typedef IOException Base;

                HARLINN_WINDOWS_DECLARE_STANDARD_EXCEPTION_MEMBERS( DirectoryNotFoundException );
            };


            class DriveNotFoundException : public IOException
            {
            public:
                typedef IOException Base;

                HARLINN_WINDOWS_DECLARE_STANDARD_EXCEPTION_MEMBERS( DriveNotFoundException );
            };

            class EndOfStreamException : public IOException
            {
            public:
                typedef IOException Base;

                HARLINN_WINDOWS_DECLARE_STANDARD_EXCEPTION_MEMBERS( EndOfStreamException );
            };

            class FileLoadException : public IOException
            {
            public:
                typedef IOException Base;

                HARLINN_WINDOWS_DECLARE_STANDARD_EXCEPTION_MEMBERS( FileLoadException );
            };


            class FileNotFoundException : public IOException
            {
            public:
                typedef IOException Base;

                HARLINN_WINDOWS_DECLARE_STANDARD_EXCEPTION_MEMBERS( FileNotFoundException );
            };   

            class PathTooLongException : public IOException
            {
            public:
                typedef IOException Base;

                HARLINN_WINDOWS_DECLARE_STANDARD_EXCEPTION_MEMBERS( PathTooLongException );
            };

            class PipeException : public IOException
            {
            public:
                typedef IOException Base;

                HARLINN_WINDOWS_DECLARE_STANDARD_EXCEPTION_MEMBERS( PipeException );
            };



        }





        HWIN_EXPORT void CheckHRESULT(HRESULT hresult);
        HWIN_EXPORT void ThrowLastOSError();
        HWIN_EXPORT void ThrowOSError(DWORD errorId);
        HWIN_EXPORT void ThrowNoInterface();
        HWIN_EXPORT void ThrowPointerIsNULL();
        HWIN_EXPORT void ThrowInvalidHandle();
        HWIN_EXPORT HRESULT HRESULTFromException(const std::exception& exception);

        template <typename T>
        inline void CheckPointerNotNull(T* ptr)
        {
            if(!ptr)
            {
                ThrowPointerIsNULL();
            }
        }

        template <typename T>
        inline void CheckPointerNotNull( std::shared_ptr<T> ptr )
        {
            if(!ptr)
            {
                ThrowPointerIsNULL();
            }
        }


    };
};

#endif //__HWINEXCEPTION_H__
